This is a local mirror of the Altera FreeCore Library. It is no longer supported.

FreeCore Library
Parameterized Functions - Advanced Features
Learn how to master the advanced features of parameterized function programming
by
Rune Baeverrud

Parameterized Functions - Advanced Features

In this tutorial we will investigate some of the more advanced features of parameterized function programming. I strongly recommend that you first read my tutorial Parameterized Functions Made Simple - there are some concepts covered there that you may find very useful, even if you already have been designing parameterized functions for a while.

We will first spend a section on investigating the LOG2( ) compile-time function which is extremely useful in parameterized function programming. Next, we will continue on to investigate some useful AHDL constructs like IF-GENERATE, FOR-GENERATE, ASSERT and USED. Lastly, we will take a quick look at the predefined DEVICE_FAMILY parameter.

For this tutorial, we will continue to build on the frequency divider function created in the first tutorial.

Using the LOG2( ) Compile-Time Function

In the parameterized example provided in the first tutorial, it should not be necessary to specify the WIDTH parameter. Given the value of the DIVIDE parameter, the number of bits required to represent DIVIDE could be calculated. There is a nifty way to do this calculation, by using the built-in LOG2( ) (logarithm base2) function. Read on...

Ask yourself: How many bits are required to hold a number with N possible values? The answer to this question is exactly what the LOG2( ) function gives us. See the table below. If you want to hold a number with 5 possible values, you would require 2.3219 bits. Of course, this is a mathematical view, so we would always round the LOG2( ) result up to the nearest whole number. The upwards rounding is what the CEIL function will do for us.

N LOG2(N) CEIL ( LOG2(N) )
3 1.5850 2
4 2.0000 2
5 2.3219 3
15 3.9069 4
16 4.0000 4
17 4.0875 5
255 7.9944 8
256 8.0000 8
257 8.0056 9

How many bits do we need to represent the number 255? Remember that if you can represent the number 255, the total number of possible values would be 256, counting the value of zero as well. Therefore, the number of bits required for holding any number N would always be:

CEIL ( LOG2 (N+1) )

We now have the knowledge to simplify the frequency divider function even further, by removing the WIDTH parameter. Remember from our example that with a DIVISOR value of 248, the highest value assigned to the Counter[] variable is (DIVIDE - 1) = 247. Therefore, in this case (N+1) equals to (247+1) = DIVIDE.

PARAMETERS 
(
  DIVIDE  = 248
);

CONSTANT DIV_TMP = (DIVIDE - 1);
CONSTANT WIDTH = CEIL ( LOG2(DIVIDE) );

SUBDESIGN div_5
(
  SysClk                  : INPUT;
  Enable                  : INPUT = VCC;
  Count[WIDTH-1..0], Zero : OUTPUT;
)
VARIABLE
  Counter[WIDTH-1..0]     : DFF;

BEGIN
  Counter[].clk = SysClk;
  Count[] = Counter[];

  IF Enable THEN
    IF Counter[] == 0 THEN
      Counter[] = DIV_TMP;
      Zero = VCC;
    ELSE
      Counter[] = Counter[] - 1;
    END IF;
  ELSE
    Counter[] = Counter[];
  END IF;
END;

Using IF-GENERATE

The div function suffers from glitches on the Zero output. Let’s add another parameter, specifying that I would like to optionally register the Zero output. By using IF-GENERATE, I can optionally declare and use an extra register for the Zero output. This is how to do it:

PARAMETERS 
(
  DIVIDE      = 248,
  GLITCH_FREE = "YES"
);

CONSTANT DIV_TMP = (DIVIDE - 1);
CONSTANT WIDTH = CEIL ( LOG2(DIVIDE) );

SUBDESIGN div_6
(
  SysClk                  : INPUT;
  Enable                  : INPUT = VCC;
  Count[WIDTH-1..0], Zero : OUTPUT;
)
VARIABLE
  Counter[WIDTH-1..0]     : DFF;

  IF GLITCH_FREE == "YES" GENERATE
    ZeroReg: DFF;
  ELSE GENERATE
    ZeroNode: NODE;
  END GENERATE;

BEGIN
  Counter[].clk = SysClk;
  Count[] = Counter[];

  IF GLITCH_FREE == "YES" GENERATE
    Zero = ZeroReg;
    ZeroReg.clk = SysClk;
  ELSE GENERATE
    Zero = ZeroNode;
  END GENERATE;

  IF Enable THEN
    IF Counter[] == 0 THEN
      Counter[] = DIV_TMP;

      IF GLITCH_FREE == "YES" GENERATE
        ZeroReg = VCC;
      ELSE GENERATE
        ZeroNode = VCC;
      END GENERATE;

    ELSE
      Counter[] = Counter[] - 1;
    END IF;
  ELSE
    Counter[] = Counter[];
  END IF;
END;

You can see that the IF-GENERATE statement can be used in the VARIABLE section of the file as well as in the logic section. Note: In this example – when GLITCH_FREE is set to "YES", the Zero output will be delayed by one SysClk period because of the extra register inserted.

Using ASSERT

With the ASSERT statement, you can check for the validity of compile-time values like parameters, constants or evaluated expressions, and write messages to the compiler message window. Let’s add some parameter checking to the above file. This is what we would like to check for:

PARAMETERS 
(
  DIVIDE      = 248,
  GLITCH_FREE = "YES"
);

CONSTANT DIV_TMP = (DIVIDE - 1);
CONSTANT WIDTH = CEIL ( LOG2(DIVIDE) );

SUBDESIGN div_7
(
  SysClk                  : INPUT;
  Enable                  : INPUT = VCC;
  Count[WIDTH-1..0], Zero : OUTPUT;
)
VARIABLE
  Counter[WIDTH-1..0]     : DFF;

  IF GLITCH_FREE == "YES" GENERATE
    ZeroReg: DFF;
  ELSE GENERATE
    ZeroNode: NODE;
  END GENERATE;

BEGIN
  ASSERT (DIVIDE >= 3)
    REPORT "DIVIDE parameter must be equal to or
            larger than 3. It is now %"
    DIVIDE
    SEVERITY ERROR;

  ASSERT (GLITCH_FREE == "YES" OR GLITCH_FREE == "NO")
    REPORT "GLITCH_FREE parameter should be ""YES""
            or ""NO"". It is now ""%"". Compiling for
            GLITCH_FREE==""NO"""
    GLITCH_FREE
    SEVERITY WARNING;

  Counter[].clk = SysClk;
  Count[] = Counter[];

  IF GLITCH_FREE == "YES" GENERATE
    Zero = ZeroReg;
    ZeroReg.clk = SysClk;
  ELSE GENERATE
    Zero = ZeroNode;
  END GENERATE;

  IF Enable THEN
    IF Counter[] == 0 THEN
      Counter[] = DIV_TMP;

      IF GLITCH_FREE == "YES" GENERATE
        ZeroReg = VCC;
      ELSE GENERATE
        ZeroNode = VCC;
      END GENERATE;

    ELSE
      Counter[] = Counter[] - 1;
    END IF;
  ELSE
    Counter[] = Counter[];
  END IF;
END;

The ASSERT statement is executed if the test condition is false. The ’%’ character in the REPORT statement is a placeholder for parameter values that you would like to display as part of the message. The parameter names are given directly after the REPORT statement, separated by commas if more than one.

With the SEVERITY statement, you have the options to specify ERROR, WARNING or INFO. If you specify ERROR, the compilation will be aborted.

You may also place ASSERT statements in the VARIABLE section of the AHDL file, which can be very useful for checking validity of bus indexes before declaring variables. For example, you would like to abort compilation if you tried to declare a variable like Counter[WIDTH-1..0] if the parameter WIDTH is set to 0, 1 or maybe even "YES". In this particular case, the compilation would probably be aborted anyway, but sometimes you would like to use the ASSERT statement in this way to provide a more meaningful message.

You may also issue unconditional INFO messages by using the following form:

ASSERT
  REPORT "Dear Rune, How are you today?"
  SEVERITY INFO

Using FOR-GENERATE

The FOR-GENERATE is very useful for creating repetitive structures. For our continued example, I will use FOR-GENERATE to reverse the bits on the Count[] output. It is possible to simply replace

Count[] = Counter[]

with

Count[0..WIDTH-1] = Counter[WIDTH-1..0] 

but this will cause a warning message during compilation saying that I may have unintentionally reversed my bits. To avoid the warning message, you could use the FOR-GENERATE statement like this:

PARAMETERS 
(
  DIVIDE      = 248,
  GLITCH_FREE = "YES",
  REVERSE_OUTPUT = "NO"
);

CONSTANT DIV_TMP = (DIVIDE - 1);
CONSTANT WIDTH = CEIL ( LOG2(DIVIDE) );

SUBDESIGN div_8
(
  SysClk                  : INPUT;
  Enable                  : INPUT = VCC;
  Count[WIDTH-1..0], Zero : OUTPUT;
)
VARIABLE
  Counter[WIDTH-1..0]     : DFF;

  IF GLITCH_FREE == "YES" GENERATE
    ZeroReg: DFF;
  ELSE GENERATE
    ZeroNode: NODE;
  END GENERATE;

BEGIN
  ASSERT (DIVIDE >= 3)
    REPORT "DIVIDE parameter must be equal to or
            larger than 3. It is now %"
    DIVIDE
    SEVERITY ERROR;

  ASSERT (GLITCH_FREE == "YES" OR GLITCH_FREE == "NO")
    REPORT "GLITCH_FREE parameter should be ""YES""
            or ""NO"". It is now ""%"". Compiling for
            GLITCH_FREE==""NO"""
    GLITCH_FREE
    SEVERITY WARNING;

  ASSERT (REVERSE_OUTPUT == "YES" OR REVERSE_OUTPUT == "NO")
    REPORT "REVERSE_OUTPUT parameter should be ""YES""
            or ""NO"". It is now ""%"". Compiling for
            REVERSE_OUTPUT==""NO"""
    GLITCH_FREE
    SEVERITY WARNING;

  Counter[].clk = SysClk;

  IF REVERSE_OUTPUT == "YES" GENERATE
    FOR i IN 0 TO (WIDTH-1) GENERATE
      Count[i] = Counter[(WIDTH-1) - i];
    END GENERATE;
  ELSE GENERATE
    Count[] = Counter[];
  END GENERATE;

  IF GLITCH_FREE == "YES" GENERATE
    Zero = ZeroReg;
    ZeroReg.clk = SysClk;
  ELSE GENERATE
    Zero = ZeroNode;
  END GENERATE;

  IF Enable THEN
    IF Counter[] == 0 THEN
      Counter[] = DIV_TMP;

      IF GLITCH_FREE == "YES" GENERATE
        ZeroReg = VCC;
      ELSE GENERATE
        ZeroNode = VCC;
      END GENERATE;

    ELSE
      Counter[] = Counter[] - 1;
    END IF;
  ELSE
    Counter[] = Counter[];
  END IF;
END;

Using USED( )

By using the USED( ) compile-time function you can check for unused ports and take appropriate actions. Please note that the USED function cannot replace default values on the input ports.

PARAMETERS 
(
  DIVIDE      = 248,
  GLITCH_FREE = "YES",
  REVERSE_OUTPUT = "NO"
);

CONSTANT DIV_TMP = (DIVIDE - 1);
CONSTANT WIDTH = CEIL ( LOG2(DIVIDE) );

SUBDESIGN div_9
(
  SysClk                  : INPUT;
  Enable                  : INPUT = VCC;
  Count[WIDTH-1..0], Zero : OUTPUT;
)
VARIABLE
  Counter[WIDTH-1..0]     : DFF;

  IF GLITCH_FREE == "YES" GENERATE
    ZeroReg: DFF;
  ELSE GENERATE
    ZeroNode: NODE;
  END GENERATE;

BEGIN
  ASSERT (DIVIDE >= 3)
    REPORT "DIVIDE parameter must be equal to or
            larger than 3. It is now %"
    DIVIDE
    SEVERITY ERROR;

  ASSERT (GLITCH_FREE == "YES" OR GLITCH_FREE == "NO")
    REPORT "GLITCH_FREE parameter should be ""YES""
            or ""NO"". It is now ""%"". Compiling for
            GLITCH_FREE==""NO"""
    GLITCH_FREE
    SEVERITY WARNING;

  ASSERT (REVERSE_OUTPUT == "YES" OR REVERSE_OUTPUT == "NO")
    REPORT "REVERSE_OUTPUT parameter should be ""YES""
            or ""NO"". It is now ""%"". Compiling for
            REVERSE_OUTPUT==""NO"""
    GLITCH_FREE
    SEVERITY WARNING;

  Counter[].clk = SysClk;

  IF REVERSE_OUTPUT == "YES" GENERATE
    FOR i IN 0 TO (WIDTH-1) GENERATE
      Count[i] = Counter[(WIDTH-1) - i];
    END GENERATE;
  ELSE GENERATE
    Count[] = Counter[];
  END GENERATE;

  IF GLITCH_FREE == "YES" GENERATE
    Zero = ZeroReg;
    ZeroReg.clk = SysClk;
  ELSE GENERATE
    Zero = ZeroNode;
  END GENERATE;

  IF USED (Enable) GENERATE
    ASSERT
      REPORT "You have decided to use the Enable input.
              Thank you for your support."
      SEVERITY INFO;
  ELSE GENERATE
    ASSERT
      REPORT "You have decided NOT to use the Enable input.
              Please reconsider."
      SEVERITY INFO;
  END GENERATE;

  IF Enable THEN
    IF Counter[] == 0 THEN
      Counter[] = DIV_TMP;

      IF GLITCH_FREE == "YES" GENERATE
        ZeroReg = VCC;
      ELSE GENERATE
        ZeroNode = VCC;
      END GENERATE;

    ELSE
      Counter[] = Counter[] - 1;
    END IF;
  ELSE
    Counter[] = Counter[];
  END IF;
END;

Using the DEVICE_FAMILY Parameter

There is a predefined parameter in the MAX+PLUS II environment called DEVICE_FAMILY. To use it, you need to declare it in the parameters section of your parameterized file, like this:

PARAMETERS 
(
  DIVIDE      = 248,
  GLITCH_FREE = "YES",
  REVERSE_OUTPUT = "NO",
  DEVICE_FAMILY
);

You can now perform conditional compilation using the DEVICE_FAMILY parameter. For example, you may want to implement a function in two different ways, depending on whether you use a MAX 7000 device or a FLEX 10K device. Also, if you want to, you could simply output the value of the DEVICE_FAMILY parameter:

  ASSERT
    REPORT "Compiling for % device family"
    DEVICE_FAMILY
    SEVERITY INFO;

The DEVICE_FAMILY parameter may take on one of the following values, depending on which device you are compiling for: FLEX10KA, FLEX10K, FLEX8000, FLEX6000, MAX9000, MAX7000S, MAX7000E, MAX7000, MAX5000, CLASSIC.


Last updated 08 Feb 2001 12:10